﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

using MassTransit;
namespace Athena
{
    internal class RabbitMqPRCTypeCache : IDisposable
    {
        private Dictionary<Type, object> _consumerCache;

        private Dictionary<Type, IBaseRequest> _requestClientCache;

        private Dictionary<Type, IBasePublish> _publishCache;

        internal RabbitMqPRCTypeCache()
        {
            _requestClientCache = InitializeRequestClientCache();
            _consumerCache = InitializeConsumerCache();
            _publishCache = InitPublishCache();

        }

        internal RabbitMqPRCTypeCache(string path)
        {
            _requestClientCache = InitializeRequestClientCache(path);
            _consumerCache = InitializeConsumerCache(path);
            _publishCache = InitPublishCache(path);
        }

        internal Dictionary<Type, object> ConsumerCache
        {
            get
            {
                return _consumerCache;
            }
        }

        internal Dictionary<Type, IBaseRequest> RequestClientCache
        {
            get
            {
                return _requestClientCache;
            }
        }

        internal Dictionary<Type, IBasePublish> PublishCache
        {
            get
            {
                return _publishCache;
            }
        }


        /// <summary>
        /// 获取Requst Class
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal T Request<T>() where T : IBaseRequest
        {
            return (T)_requestClientCache[typeof(T)];
        }

        /// <summary>
        /// 获取Publish Class
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal T Publish<T>() where T : IBasePublish
        {
            return (T)_publishCache[typeof(T)];
        }


        private static bool TypeIsVisible(Type type)
        {
            return (type != null && type.IsVisible);
        }

        private static bool IsRabbitMQType<T>(Type type)
        {
            return !type.IsAbstract && typeof(T).IsAssignableFrom(type);
        }

        private static bool IsRabbitMQType(Type unKnowType, Type type)
        {
            return !unKnowType.IsAbstract && unKnowType.IsAssignableFrom(type);
        }

        private Dictionary<Type, T> InitializeCache<T>(string path = "")
        {
            var cacheDictionary = new Dictionary<Type, T>();
            Assembly assembly = null;
            if (string.IsNullOrWhiteSpace(path))
            {
                assembly = Assembly.GetEntryAssembly();
            }
            else
            {
                assembly = Assembly.Load(path);
            }
            //foreach (var assembly in assemblies)
            //{
            if (assembly == null || assembly.IsDynamic)
            {
                // can't call GetTypes on a null (or dynamic?) assembly
                return cacheDictionary;
            }
            var exportedTypes = assembly.GetTypes();

            var range = exportedTypes.Where(lm => TypeIsVisible(lm) && IsRabbitMQType<T>(lm));
            if (range != null && range.Count() > 0)
            {
                foreach (var item in range)
                {
                    //NewExpression newInstanceExpression = Expression.New(item);
                    //var activator = (Func<T>)Expression.Lambda(newInstanceExpression).Compile();
                    //cacheDictionary.Add(item, activator());
                    cacheDictionary.Add(item, (T)Activator.CreateInstance(item, new object[] { Guid.NewGuid() }));
                }
            }
            //}
            return cacheDictionary;
        }

        private Dictionary<Type, IBaseRequest> InitializeRequestClientCache(string path = "")
        {
            return InitializeCache<IBaseRequest>(path);
        }

        private Dictionary<Type, IBasePublish> InitPublishCache(string path = "")
        {
            return InitializeCache<IBasePublish>(path);
        }

        private Dictionary<Type, object> InitializeConsumerCache(string path = "")
        {
            var cacheDictionary = new Dictionary<Type, object>();
            Assembly assembly = null;
            if (string.IsNullOrWhiteSpace(path))
            {
                assembly = Assembly.GetEntryAssembly();
            }
            else
            {
                assembly = Assembly.Load(path);
            }
            if (assembly == null || assembly.IsDynamic)
            {
                return cacheDictionary;
            }
            var exportedTypes = assembly.GetTypes();
            var range = exportedTypes.Where(x => x.IsVisible && !x.IsAbstract && x.GetInterfaces().Any(y => y.IsGenericType && y.GetGenericTypeDefinition() == typeof(IConsumer<>)))
                .ToArray();

            if (range != null && range.Count() > 0)
            {
                foreach (var item in range)
                {
                    cacheDictionary.Add(item, Activator.CreateInstance(item));
                }
            }
            return cacheDictionary;
        }

        public void Dispose()
        {
            _consumerCache = null;
            _requestClientCache = null;
            _publishCache = null;
            GC.Collect();
        }
    }
}
